//////////////////////////

using System;
using System.IO;
using Newtonsoft.Json;  //引用命名空间
using System.Collections.Generic;
using System.Collections;
using System.Reflection;
// #nullable enable
using UnityEngine;
using UnityEngine.Networking;
using System.Text.RegularExpressions;


public class BdUtils
{
    public static string NULL_TYPE = "null";       // get_type(null)的返回值
    // public static float DEFAULT_INT = -99999999;
    public const int DEFAULT_INT = -9876789;
    public const float DEFAULT_FLOAT = (float)DEFAULT_INT;
    // public const Vector3 DEFAULT_VECTOR3 = DEFAULT_INT * Vector3.one;

    // public static float DEFAULT_FLOAT = (float)DEFAULT_INT;
    // public static float DEFAULT_FLOAT = -9999999;
    // public static float DEFAULT_FLOAT = -9999999;
    public static string seq_print = "";

    public static string conv_obj_to_string(object obj, string default_ret = "")
    {
        string res = default_ret;
        if (!BdUtils.is_null(obj))
            res = obj.ToString();
        return res;
    }

    #region --- file path utils

    public static string streamingAssetsPath = Application.streamingAssetsPath;
    public static string data_table_dir_path = join(streamingAssetsPath, "data");
    public static string streaming_data_dir_path = join(streamingAssetsPath, "data");

    public static string _persistentDataPath;
    public static string persistentDataPath
    {
        get
        {
            if (_persistentDataPath == null)
            {
                BdUtils.get_base_info();
            }
            return _persistentDataPath;
        }
    }
    static string _persistant_data_dir_path;
    public static string persistant_data_dir_path
    {
        get
        {
            if (_persistant_data_dir_path == null)
            {
                _persistant_data_dir_path = join(persistentDataPath, "data");
                if (!Directory.Exists(_persistant_data_dir_path))     // 返回bool类型，存在返回true，不存在返回false
                {
                    Directory.CreateDirectory(_persistant_data_dir_path);      //不存在则创建路径
                }
            }
            return _persistant_data_dir_path;
        }
    }

    /// <summary>
    /// 指定文件路径`f_path`, 检测文件后缀名是否为`target_extension`
    /// </summary>
    public static bool check_file_extension(string f_path, string target_extension = ".csv", bool raise_error = true)
    {
        List<string> target_extension_ls = new List<string>();
        target_extension_ls.Add(target_extension);

        return check_file_extension(f_path, target_extension_ls, raise_error);
    }

    public static string get_relative_sub_path(string parent_path, string sub_path, bool raise_error = false)
    {
        if (!sub_path.StartsWith(parent_path) || sub_path == parent_path)
        {
            if (raise_error)
            {
                raise_exception_error_func($"sub_path[{sub_path}] 不是 parent_path[{parent_path}] 的子路径");
            }
            return sub_path;
        }

        sub_path = sub_path.Substring(parent_path.Length, sub_path.Length - parent_path.Length);
        sub_path = sub_path.Trim('/').Trim('\\');
        return sub_path;
    }

    /// <summary>
    /// 获得`persistant_file_path`: 将一个`streaming_file_path`中的`streaming_data_dir_path`替换为`persistant_data_dir_path`
    /// 为安卓端的兼容设计
    /// </summary>
    /// <param name="streaming_file_path">`streaming_data_dir_path`下的文件路径</param>
    /// <returns></returns>
    public static string replace_streaming_file_path_to_persistant_file_path(string streaming_file_path)
    {
        BdUtils.assert(streaming_file_path.StartsWith(streaming_data_dir_path), $"*** streaming_file_path[{streaming_file_path}]不是streaming_data_dir_path[{streaming_data_dir_path}]的子路径?");
        string relative_path = get_relative_sub_path(streaming_data_dir_path, streaming_file_path);

        string persistant_file_path = BdUtils.join(persistant_data_dir_path, relative_path);
        return persistant_file_path;
    }
    
    public static Tuple<string, string> get_file_name_and_extension(string f_path)
    {
        return new Tuple<string, string>(Path.GetFileNameWithoutExtension(f_path), Path.GetExtension(f_path));
    }

    public static string complete_file_extension(string file_extension)
    {
        string res = file_extension.StartsWith(".")? file_extension : $".{file_extension}";
        return res;
    }
    
    /// <summary>
    /// 指定文件路径`f_path`, 检测文件后缀名是否在列表`target_extension_ls`之中
    /// </summary>
    public static bool check_file_extension(string f_path, List<string> target_extension_ls, bool raise_error = true)
    {
        // --- 检查文件类型是否合格
        bool ret = false;

        if (target_extension_ls.Count == 0)
        {
            target_extension_ls.Add(".csv");
        }

        string file_extension = Path.GetExtension(f_path);

        foreach (var target_extension_i in target_extension_ls)
        {
            var _target_extension_i = complete_file_extension(target_extension_i);
            if (file_extension.EndsWith(_target_extension_i))
            {
                ret = true;
            }
        }

        if (raise_error)
            BdUtils.assert(ret, $"方法仅支持读取`{BdUtils._log(target_extension_ls)}`格式的文件, 不支持`{file_extension}`格式!");

        // if (file_extension.EndsWith(target_extension) == false)
        // {
        //     if (raise_error)
        //         BdUtils.assert(false, $"方法`read_csv`仅支持读取`{target_extension}`格式的文件, 不支持`{file_extension}`格式!");
        //     ret = false;
        // }
        return ret;
    }

    public static void copy_file_from_streaming_to_persistant(string input_file_name, string output_file_name = null, bool overwrite = false)
    {
// #if !UNITY_EDITOR && UNITY_ANDROID

        if (BdTools.Flags.is_android_or_webgl)
        {
            string file_name = Path.GetFileName(input_file_name);
            string dirname = "data";

            string _dirname = Path.GetDirectoryName(input_file_name);
            // BdUtils.assert(_dirname == null, "*** input_file_name Error: 安卓端的`_dirname`必须为空!");
            if (_dirname != null)
            {
                dirname = BdUtils.join(dirname, _dirname);
            }

            BdUtils.log("*** 这里是安卓设备! input_file_name:", input_file_name, ", dirname:", dirname);

            BdUtils.assert(output_file_name == null, "安卓设备时不能自定义`output_file_name`!");
            andriod_copy_file_from_streaming_to_persistant(file_name, dirname);
            return;
        }
// #endif

        string input_file_path = join(data_table_dir_path, input_file_name);
        string output_file_path = join(persistant_data_dir_path, input_file_name);

        if (output_file_name == null)
            output_file_name = input_file_name;

        BdUtils.log(" --- on copy_file_from_streaming_to_persistant, input_file_name:", input_file_name, ", overwrite:", overwrite);

        BdUtils.assert(File.Exists(input_file_path), $"`input_file_path[{input_file_path}]`不存在?");

        if (File.Exists(output_file_path))
        {
            if (!overwrite)
            {
                return;
            }
        }
        string output_dir_path = Path.GetDirectoryName(output_file_path);
        if (output_dir_path != null && !Directory.Exists(output_dir_path))
        {
            Directory.CreateDirectory(output_dir_path);
        }
        BdUtils.assert(Directory.Exists(output_dir_path), $"output_dir_path[{output_dir_path}]不存在?");

        BdUtils.log(" ~~~ on copy_file_from_streaming_to_persistant, input_file_name:", input_file_name);
        var content = File.ReadAllBytes(input_file_path);
        File.WriteAllBytes(output_file_path, content);
    }

    /// <summary>
    /// [安卓端]
    /// 将指定的文件从`streaming`解压到`persistent`下
    /// </summary>
    /// <param name="fileName">原文件路径</param>
    /// <param name="dirname">原文件夹路径</param>
    /// <param name="output_file_name">输出名, 默认为原文件名</param>
    /// <returns></returns>
    public static string andriod_copy_file_from_streaming_to_persistant(string fileName, string dirname = "data", string output_file_name = null)
    {
        if (fileName.Contains("/"))
        {
            string _directory = Path.GetDirectoryName(fileName);
            string _filename = Path.GetFileName(fileName);
                
            dirname = BdUtils.join(dirname, _directory);
            fileName = _filename;
        }
        
        BdUtils.log("*** 安卓 read_csv_data_in_all_platform! file_name:", fileName, ", dirname:", dirname);

        string fromPath;
        string toPath;

        // DirectoryInfo _path = new DirectoryInfo(fileName);

        // BdUtils.log("_path.Parent ---", _path.Parent);

        // string dir_path = "";
        // if (_path.Parent != null)
        //     dir_path = _path.Parent.FullName;//上 2层目录


        // string dirname = Path.GetDirectoryName(fileName);

        // string dir_path = Application.persistentDataPath + '/' + dirname;


        fromPath = Path.Combine(Application.streamingAssetsPath, dirname, fileName);
        // if (output_file_name == null)
        // {
        //     output_file_name = fileName;
        // }
        toPath = Path.Combine(Application.persistentDataPath, dirname, fileName);

        if (dirname != "")
        {
            string dir_path = Path.Combine(Application.persistentDataPath, dirname);
            // BdUtils.log("dir_path ---", dir_path);
            if (!Directory.Exists(dir_path))
                Directory.CreateDirectory(dir_path);
        }

        // fromPath = Application.streamingAssetsPath + "/" + fileName;
        // toPath = Application.persistentDataPath + "/" + fileName;

        // BdUtils.log("*** fromPath ---", fromPath);
        // BdUtils.log("*** toPath ---", toPath);

        # region 旧版本`WWW`, 新版本用`UnityWebRequest`
        // WWW w = new WWW(fromPath);

        // while (!w.isDone) { }

        // if (w.error == null)
        // {
        //     File.WriteAllBytes(toPath, w.bytes);
        // }

        # endregion

        UnityWebRequest request = UnityWebRequest.Get(fromPath);
        request.SendWebRequest();//读取数据

        while (!request.isDone) { }

        if (request.error == null)//是否读取完数据
        {
            File.WriteAllBytes(toPath, request.downloadHandler.data);

            // if (check_file_extension(toPath, new List<string>(){"png", "jpg", "jpeg"}, raise_error: false))
            //     File.WriteAllBytes(toPath, request.downloadHandler.data);
            // else
            //     File.WriteAllText(toPath, request.downloadHandler.text);
        }

        return toPath;
    }

    public static char seq_path = Path.DirectorySeparatorChar;
    public static string base_temp_dir_name = "base_tempdir";
    public static string base_resource_dir_name = "base_resource";

    static string _base_temp_dir_path;      // 基本临时文件根目录
    static string _base_resource_dir_path;      // 基本资源文件根目录

    public static string _resourcesPath;
    
    public static bool create_file_if_not_exists(string filePath)
    {
        try
        {
            // 检查文件是否存在
            if (!File.Exists(filePath))
            {
                // 获取文件的目录路径
                string directoryPath = Path.GetDirectoryName(filePath);

                // 如果目录不存在，则创建它
                if (!Directory.Exists(directoryPath))
                {
                    Directory.CreateDirectory(directoryPath);
                }

                // 创建文件
                File.Create(filePath).Close();
                return true; // 文件创建成功
            }
            else
            {
                return true; // 文件已存在
            }
        }
        catch (Exception ex)
        {
            BdUtils.raise_exception_error_func($"创建文件时发生错误: {ex.Message}");
            return false; // 文件创建失败
        }
    }
    public static string resourcesPath
    {
        get
        {
            if (_resourcesPath == null)
            {
                BdUtils.get_base_info();
            }
            return _resourcesPath;
        }
    }

    public static string base_temp_dir_path
    {
        get
        {
            if (_base_temp_dir_path == null)
            {
                BdUtils.get_base_info();
            }
            return _base_temp_dir_path;
        }
    }

    public static string base_resource_dir_path
    {
        get
        {
            if (_base_resource_dir_path == null)
            {
                BdUtils.get_base_info();
            }
            return _base_resource_dir_path;
        }
    }

    public static int get_decimal_places(float number)
    {
        int decimalPlaces = BitConverter.GetBytes(decimal.GetBits((decimal)number)[3])[2];
        return decimalPlaces;
    }

    public static void get_base_info()  // 等unity框架初始化完成才能调用相关方法
    {
        _persistentDataPath = Application.persistentDataPath;
        _base_temp_dir_path = BdUtils.join(Application.persistentDataPath, base_temp_dir_name);
        _base_resource_dir_path = BdUtils.join(Application.persistentDataPath, base_resource_dir_name);
        _resourcesPath = BdUtils.join("Assets", "Resources");
    }

    public static void call(object obj)
    {
    }

    /// <summary>
    /// 初学时的简化版, 后面发现可用`Path.Join`代替
    /// </summary>
    public static string join(params string[] paths)
    {
        string res = System.IO.Path.Combine(paths);
        res = res.Replace("\\", "/");
        return res;
    }

    public static string unity_join(params string[] paths)
    {
        return join(paths).Replace("\\", "/");
    }
    
    public static bool exists_file(string path)
    {
        if (BdTools.Flags.is_android_or_webgl)
        {
            if (path.StartsWith(BdUtils.streamingAssetsPath))
            {
                string rel_path = BdUtils.get_relative_sub_path(BdUtils.streaming_data_dir_path, path);
                // BdUtils.log("~~~~~~~~~ path.StartsWith(BdUtils.streamingAssetsPath):", path.StartsWith(BdUtils.streamingAssetsPath), "\n --- rel_path:", rel_path);
            }
        }
        
        // if (BdTools.Flags.is_android && path.StartsWith(BdUtils.streaming_data_dir_path) )
        // {
        //     // BdUtils.persistant_data_dir_path
        //     BdUtils.andriod_copy_file_from_streaming_to_persistant()
        //     
        // }
        // // 如果是在 Android 设备上
        // if (Application.platform == RuntimePlatform.Android && (path.Contains("://") || path.Contains(":///")))
        // {
        //     // StreamingAssets 中的文件在打包后会被压缩到 APK 中，因此需要使用 UnityWebRequest 来访问
        //     UnityWebRequest www = UnityWebRequest.Get(path);
        //     www.SendWebRequest();
        //
        //     while (!www.isDone) { }
        //
        //     // if (www.isNetworkError || www.isHttpError)
        //     if (www.result == UnityWebRequest.Result.ProtocolError)
        //     {
        //         Debug.Log("exists_file.get_file_error:" +　www.error);
        //         return false;
        //     }
        //     else
        //     {
        //         // 文件存在
        //         return true;
        //     }
        // }
        return File.Exists(path);
    }
    public static bool exists_directory(string path)
    {
        return Directory.Exists(path);
    }
    
    #endregion

    // public static void Log(object obj_i)
    // {
    //     log(obj_i);
    // }

    public static void log(object obj_i)
    {
        if (is_list_or_dict(obj_i))
        {
            string json_str = JsonConvert.SerializeObject(obj_i);
            sys_print(json_str);
            return;
        }

        var obj_type = BdUtils.get_type(obj_i);

        if (obj_type == BdUtils.NULL_TYPE)
        {
            sys_print(obj_type);
        }
        else if (obj_type != "System.String" && obj_i is IEnumerable)
        {
            List<object> list = new List<object>();
            var enumerator = ((IEnumerable)obj_i).GetEnumerator();
            while (enumerator.MoveNext())
            {
                list.Add(enumerator.Current);
            }
            foreach (var kv in list)
            {
                sys_print(kv.ToString());
            }
        }
        else
        {
            sys_print(obj_i.ToString());
        }
    }

    public static void test_f(object a)
    {
        string ss = string.Format("--- a: {0} --- type: {1}", a, a.GetType());
        Console.WriteLine(ss);

        // string s2 = string.Format("--- ss: {0} --- type: {1}", ss, ss.GetType());
        // Console.WriteLine(s2);
        // Console.WriteLine(a);
    }

    //Dictionary转json
    public static Dictionary<string, object> test1(object js)
    {
        string json_str = js.ToString();
        Dictionary<string, object> dc = JsonConvert.DeserializeObject<Dictionary<string, object>>(json_str);
        BdUtils.log(json_str, dc);
        return dc;
    }

    public static string conv_obj_to_json(object obj, bool ignore_error = false)
    {
        string ret;
        if (ignore_error)
        {
            ret = JsonConvert.SerializeObject(obj, Formatting.None, new JsonSerializerSettings()
            {
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore
            });
        }
        else
            ret = JsonConvert.SerializeObject(obj);
        return ret;
    }
    //json转Dictionary
    public static Dictionary<string, object> conv_json_to_dict(object json, object key_ls = null)
    {
        /*
        给定json字符串, 转化为字典.
        若列表key_ls不为null, 则按key_ls的顺序进行取值再返回字典dc

        --- eg:
        >>> var dc = BdUtils.conv_json_to_dict(json, new string[] { "result", "page_dc" });
        >>> var dc = BdUtils.conv_json_to_dict(json, new List<string> { "result", "page_dc" });
        */
        var _key_ls = conv_obj_to_list(key_ls);
        var dc = _conv_json_to_dict(json, _key_ls);
        return dc;
    }
    private static Dictionary<string, object> _conv_json_to_dict(object json, List<object> key_ls = null)
    {
        assert(!is_null(json), "UserAssertError: json不能为null!");
        string json_str = json.ToString();
        Dictionary<string, object> dc;

        if (BdUtils.is_dict(json))
        {
            dc = conv_obj_to_dict(json);
        }
        else
            try
            {
                dc = JsonConvert.DeserializeObject<Dictionary<string, object>>(json_str);
            }
            catch (Exception)
            {
                // log($"------ catch Exception:\n{ex}\n ------ \n\n");
                throw new Exception($"*** Error: json_str转化为dict失败! 请检查对应的json_str是否标准json格式, json_str: \"{json_str}\"");
            }

        if (key_ls != null)
        {
            foreach (var k in key_ls)
            {
                json_str = dc[k.ToString()].ToString();
                // log("========", k.ToString(), k.GetType());
                dc = _conv_json_to_dict(json_str);
            }
        }
        return dc;
    }
    private static List<object> _conv_json2list(object json)
    {
        if (BdUtils.is_list(json))
        {
            throw new Exception("这本来就是个列表了, 你玩我呢大哥?");
        }

        string json_str = json.ToString();
        List<object> ls = JsonConvert.DeserializeObject<List<object>>(json_str);
        // if (key_ls != null)
        // {
        //     foreach (var k in key_ls)
        //     {
        //         // k是整数的话, 就将此时的json用list解释, 否则用dict
        //         if (is_integer(k))
        //         {
        //             json_str = dc[k.ToString()].ToString();
        //             dc = _conv_json2list(json_str);
        //         }
        //         else
        //         {
        //             json_str = dc[k.ToString()].ToString();
        //             dc = _conv_json_to_dict(json_str);
        //         }
        //         json_str = dc[k.ToString()].ToString();
        //         log("========", k.ToString(), k.GetType());
        //         dc = _conv_json_to_dict(json_str);
        //     }
        // }
        return ls;
    }

    public static string get_type(object obj)
    {
        return BdUtils.py_type(obj);
    }
    public static string py_type(object obj)
    {
        if (obj == null)
        {
            return NULL_TYPE;
        }
        return obj.GetType().ToString();
    }

    public static bool is_decimal(object obj)
    {
        string obj_type = BdUtils.get_type(obj);

        // 是否为小数
        List<string> decimal_ls_types = new List<string>() { "System.Single", "System.Double" };
        if (decimal_ls_types.Contains(obj_type))
        {
            return true;
        }
        return false;
    }

    public static string conv_to_string(object obj)
    {
        string ret = "";
        if (!is_null(obj))
            ret = obj.ToString();
        return ret;
    }

    public static int conv_to_int(object obj)        // , int default_ret = null   |   Nullable<int>
    {
        var obj_type = BdUtils.get_type(obj);
        int ret;
        BdUtils.assert(!is_null(obj), "obj不能为空, 请指定默认值!");
        BdUtils.assert(int.TryParse(obj.ToString(), out ret), $"obj[{obj}]转换int失败!");
        return ret;
    }
    
    public static int conv_to_int(object obj, int default_ret)        // , int default_ret = null   |   Nullable<int>
    {
        var obj_type = BdUtils.get_type(obj);
        int ret;
        if (obj_type == BdUtils.NULL_TYPE || !int.TryParse(obj.ToString(), out ret))
        {
            return default_ret;
        }
        else
        {
            return ret;
        }
    }

    // public static int? conv_to_int(object obj, object default_ret = null)        // , int default_ret = null   |   Nullable<int>
    // {
    //     var obj_type = BdUtils.get_type(obj);
    //     int ret;
    //     if (obj_type == BdUtils.NULL_TYPE || !int.TryParse(obj.ToString(), out ret))
    //     {
    //         return (int?)default_ret;
    //     }
    //     else
    //     {
    //         return ret;
    //     }
    // }
    
    public static float conv_to_float(object obj)        // , int default_ret = null   |   Nullable<int>
    {
        var obj_type = BdUtils.get_type(obj);
        float ret;
        BdUtils.assert(!is_null(obj), "obj不能为空, 请指定默认值!");
        BdUtils.assert(float.TryParse(obj.ToString(), out ret), $"obj[{obj}]转换`float`失败!");
        return ret;
    }
    
    public static float conv_to_float(object obj, float default_ret)        // , int default_ret = null   |   Nullable<int>
    {
        var obj_type = BdUtils.get_type(obj);
        float ret = DEFAULT_FLOAT;
        if (obj_type == BdUtils.NULL_TYPE || !float.TryParse(obj.ToString(), out ret))
        {
            return default_ret;
        }
        else
        {
            return ret;
        }
    }
    public static bool is_integer(object obj)
    {
        string obj_type = BdUtils.get_type(obj);

        // 是否为整数
        List<string> integer_ls_types = new List<string>() { "System.Int32", "System.Int64" };
        if (integer_ls_types.Contains(obj_type))
        {
            return true;
        }
        return false;
    }

    public static bool is_number(object obj)
    {
        return BdUtils.is_decimal(obj) || BdUtils.is_integer(obj);
    }

    public static double conv_obj_to_number(object obj)
    {
        // log("=== sleep_obj: ", obj, obj.ToString(), double.Parse(obj.ToString()));
        return double.Parse(obj.ToString());
    }

    public static bool is_bool(object obj)
    {
        bool ret = true;
        string obj_type = BdUtils.get_type(obj);
        if (obj_type != "System.Boolean")
        {
            ret = false;
        }
        return ret;
    }

    public static bool conv_to_bool(object obj)
    {
        if (is_null(obj))
            return false;
        
        bool ret = true;
        string obj_type = BdUtils.get_type(obj);
        if (is_bool(obj))
        {
            return Convert.ToBoolean(obj);
        }
        else if (obj_type == NULL_TYPE)
        {
            ret = false;
        }
        else if (BdUtils.is_number(obj))
        {
            if (conv_obj_to_number(obj) == 0)
                ret = false;
        }
        else if (obj_type == "System.String")
        {
            if (obj.ToString().Length == 0)
                ret = false;
        }
        else if (obj_type == "System.DBNull")
        {
            ret = false;
        }
        else if (BdUtils.is_list(obj) || is_dict(obj))
        {
            List<object> collection = BdUtils.conv_obj_to_list(obj);
            if (collection.Count == 0)
                ret = false;
        }

        // BdUtils.log("------------- obj, ret, obj_type: ", obj, ret, obj_type);   //, int.Parse(obj)
        return ret;
    }

    public static bool is_system_ls(object obj)
    {
        // 判断是否为system.string[]之类的东东
        var ret = false;
        var obj_type = get_type(obj);
        if ((obj_type.StartsWith("System") && obj_type.EndsWith("[]")) && !is_dict(obj))
            ret = true;
        return ret;
    }

    public static bool is_string(object obj)
    {
        var obj_type = py_type(obj);
        bool ret = false;
        if (obj_type == "System.String")
            ret = true;
        return ret;
    }

    public static bool is_list(object obj)
    {
        // 只判断是不是[List, int[], float[], string[]]等基本类型
        var ret = false;
        var obj_type = py_type(obj);

        if (!is_string(obj) && obj is IEnumerable)
        {
            List<string> ls_types = new List<string>() { "System.Collections.Generic.List" };
            if (is_system_ls(obj))
                ret = true;
            else
            {
                // 这里可能有其它类型的list, 留待以后填坑
                foreach (var tp in ls_types)
                {
                    if (obj_type.StartsWith(tp))
                    {
                        ret = true;
                        break;
                    }
                }
            }
        }

        return ret;
    }

    public static bool is_list_or_dict(object obj)
    {
        return is_dict(obj) || is_list(obj);
    }

    public static bool is_dict(object obj)
    {
        var ret = false;
        var obj_type = py_type(obj);
        if (obj_type.StartsWith("System.Collections.Generic.Dictionary"))
        {
            ret = true;
        }

        return ret;
    }

    public static bool raise_exception_error_func(string message = "User error!")
    {
        throw new UnityException(message);
    }

    public static bool assert(object sentence, string message = "Sentence error!", bool raise_error = true)
    {
        bool flag = conv_to_bool(sentence);

        // if (!flag) throw new Exception(message);
        // System.Diagnostics.Debug.Assert(flag, message);
        // Debug.Assert(flag, message);  // 这个不暂停, 只报错

        if (!flag)
        {
            if (raise_error)
                throw new UnityException(message);
            else
            {
                log(message);
                return true;
            }
        }

        return false;
    }

    public static bool not_null(object obj)
    {
        return !is_null(obj);
    }

    public static TValue py_get_dc_value<TKey, TValue>(Dictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue = default)
    {
        TValue value;
        if (dictionary.TryGetValue(key, out value))
        {
            return value;
        }
        else
        {
            return defaultValue;
        }
    }
    public static bool is_null(object obj)
    {
        var ret = false;
        var obj_type = py_type(obj);
        
        if (obj_type == "null")
        {
            ret = true;
        }
        else if (obj_type == "System.String")
        {
            ret = string.IsNullOrEmpty(obj.ToString());
        }
        else
        {
            if (is_dict(obj))
            {
                if (conv_obj_to_dict(obj).Count == 0)
                    ret = true;
            }
            if (is_list(obj))
            {
                if (conv_obj_to_list(obj).Count == 0)
                    ret = true;
            }
            
            // 如果是Unity组件，则使用Unity的方式来判断是否为空
            if (obj is Component)
            {
                ret = (Component)obj == null;
                // BdUtils.log($"~~~ Component --- obj_type: {obj_type} --- ret: {ret}");
            }
        }

        return ret;
    }

    public static void py_print(params object[] list)
    {
        BdUtils.log(list);
    }

    public static List<T> conv_obj_to_list<T>(object obj)
    {
        List<T> ls2 = new List<T>();
        var ls1 = conv_obj_to_list(obj);
        foreach (var item in ls1)
        {
            if (item is T)
            {
                ls2.Add((T)item);
            }
            else
            {
                try
                {
                    T convertedValue;
                    string tp_name = get_type(item);
                    if (tp_name == "System.Data.DataColumn" || tp_name == "OfficeOpenXml.ExcelWorksheet")
                        convertedValue = (T)Convert.ChangeType(item.ToString(), typeof(T));
                    else
                        convertedValue = (T)Convert.ChangeType(item, typeof(T));
                    ls2.Add(convertedValue);
                }
                catch (InvalidCastException)
                {
                    // Handle the error
                    log(" *** `conv_obj_to_list`转换list类型失败", "--- item:", item, "--- type:", get_type(item));
                }
            }
        }

        return ls2;
    }

    public static List<object> conv_obj_to_list(object obj)
    {
        if (get_type(obj) == NULL_TYPE)
            return null;

        List<object> collection = new List<object>();

        if (is_string(obj))     // 字符串的话直接包一层就返回
        {
            collection.Add(Convert.ToString(obj));
            return collection;
        }

        var enumerator = ((IEnumerable)obj).GetEnumerator();
        while (enumerator.MoveNext())
        {
            collection.Add(enumerator.Current);
        }
        return collection;
    }
    public static Dictionary<string, object> conv_obj_to_dict(object obj)
    {
        if (get_type(obj) == NULL_TYPE)
            return null;
        Dictionary<string, object> collection = new Dictionary<string, object>();
        var json_str = JsonConvert.SerializeObject(obj);
        var dc = JsonConvert.DeserializeObject<Dictionary<string, object>>(json_str);
        return dc;
    }

    public static string to_js(object obj)
    {
        return JsonConvert.SerializeObject(obj);
    }

    public static string _log(params object[] list)
    {

        string ret = "";
        var seq = seq_print;

        for (int i = 0; i < list.Length; i++)
        {
            var obj_i = list[i];

            if (is_list_or_dict(obj_i))
            {
                string[] _seq_ls = { "[", "]" };
                string[] _seq_dc = { "{", "}" };

                var _seq = _seq_ls;
                if (is_dict(obj_i))
                {
                    _seq = _seq_dc;
                }
                ret += _seq[0];

                List<object> collection = conv_obj_to_list(obj_i);
                for (int j = 0; j < collection.Count; j++)
                {
                    // print("--- get_type collection[j]: " + get_type(collection[j]));
                    var _tmp = collection[j];
                    if (is_list_or_dict(_tmp))
                        ret += to_js(_tmp);
                    else
                        ret += _tmp;
                    if (j != collection.Count - 1)
                    {
                        ret += seq + ' ';
                    }
                }
                ret += _seq[1] + seq + ' ';

            }
            else
            {
                if (obj_i == null)
                {
                    ret += NULL_TYPE;
                }
                else
                {
                    ret += obj_i;
                }
                if (i != list.Length - 1)
                {
                    ret += seq + ' ';
                }
            }
        }
        return ret;
    }

    // public static string end_string = String.Concat(System.Linq.Enumerable.Repeat(" ", 30));  // 在使用Console.Clear()时有奇用
    public static string end_string = repeat_str(" ", 30);  // 在使用Console.Clear()时有奇用

    public static string repeat_str(string ss, int repeat_times)
    {
        string ret = String.Concat(System.Linq.Enumerable.Repeat(ss, repeat_times));
        return ret;
    }

    // 写入log日志
    public static string _log_file_path = "Assets/StreamingAssets/logs/test.log";

    // public static string get_delegated_method_full_name<T>(T method_i)
    // {
    //     return $"{method_i.Method.DeclaringType.FullName}+{method_i.Method.Name}";
    // }
    public static void log(params object[] list)
    {
        string ret = _log(list);
        sys_print(ret);

        if (is_write_log_info)
        {
            // 是否在首次写入时初始化日志信息
            if (is_init_log_info)
            {
                init_log_info();
            }

            // 写入日志队列
            lock (_log_write_queue)
                _log_write_queue.Enqueue(ret);

            // 开始写入日志
            _write_msg_to_log_file();

            is_init_log_info = false;
        }
    }

    public static float round(float value, int digits = 0)
    {
        float ret = (float)Math.Round(value, digits);
        return ret;
    }

    public static void init_log_info()
    {
        int retry = 0;
        while (retry < 10)
        {
            try
            {
                if (File.Exists(_log_file_path))
                    File.WriteAllText(_log_file_path, string.Empty);
                else
                    File.Create(_log_file_path);
                // File.Delete(logFile);
                retry += 1;
            }
            catch (IOException)
            {
                //file is in use, wait and try again
                System.Threading.Thread.Sleep(100);
            }
        }
        if (retry >= 10)
            sys_print("*** 错误`retry`超过上限!");

    }

    public static bool is_write_log_info = false;
    public static bool is_init_log_info = true;
    static Queue<string> _log_write_queue = new Queue<string>();
    public static void sys_print(string ret)
    {
        Debug.Log(ret);
        // Console.WriteLine(ret);
    }

    public static void _write_msg_to_log_file()
    {
        lock (_log_write_queue)
            if (_log_write_queue.Count != 0)
            {
                string msg = _log_write_queue.Dequeue();
                // BdUtils.log($"--- net_commands action --- {action}", BdUtils.end_string);
                // BdUtils.TimeUtils.sleep(0.2);

                using (StreamWriter writer = new StreamWriter(File.Open(_log_file_path, FileMode.Append, FileAccess.Write), System.Text.Encoding.UTF8))
                {
                    writer.WriteLine(TimeUtils.get_time_string() + ": " + msg);
                }
            }
    }

    public class TimeUtils
    {
        public static void sleep(object t)
        {
            // 休眠t秒钟
            int _t = (int)(BdUtils.conv_obj_to_number(t) * 1000);
            // log("~~~~~~~ sleep t:", _t);
            System.Threading.Thread.Sleep(_t);
        }
        public static double time(bool ms = true)
        {
            double ret;
            if (!ms)
            {
                ret = BdUtils.TimeUtils.GetTimeStamp();
            }
            else
            {
                ret = BdUtils.TimeUtils.GetTimeStampMs();
                // ret = long.Parse(BdUtils.TimeUtils.GetTimeStampMs().ToString().Replace(".", ""));
            }
            ret = BdUtils.conv_obj_to_number(ret);
            return ret;
        }

        public static string get_unique_suffix(bool ms = true, int random_suffix_len = 4, int ret_length = 18)
        {
            // --- 获取一个独一无二的文件名后缀, 后缀头为当前时间戳, random_suffix_len为时间戳后随机后缀的长度, ret_length固定返回长度
            string ret = null;


            // System.Random rd = new System.Random();
            System.Random rd = new System.Random(Guid.NewGuid().GetHashCode());     // 解决并发问题

            // ret = GetTimeStampMs().ToString().Replace(".", "");
            ret = get_time_string("yyyyMMddhhmmssffff");
            while (ret.Length < ret_length)
            {
                ret += "0";
            }
            ret = ret.Substring(0, ret_length);

            if (random_suffix_len > 0)
            {
                string temp = "";
                for (var i = 0; i < random_suffix_len; i++)
                    temp += rd.Next(0, 10).ToString();
                ret += temp;
            }

            return ret;
        }

        // 获取当前时间戳--10位时间戳, 注意,int32的时间戳, 只能到2038年, 所以采用了long(int64) 
        public static long GetTimeStamp()
        {
            // 注意, 如果直接使用DateTime.Now, 会有系统时区问题, 导致误差
            TimeSpan timeStamp = DateTime.Now.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToInt64(timeStamp.TotalSeconds);
        }

        // 获取当前时间戳, 包含毫秒数
        public static double GetTimeStampMs()
        {
            double timeStamp = ((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000) * 0.001;
            return timeStamp;
        }

        // Nullable<string> string?

        public static string get_time_format(string dtype = "ds", bool? get_date = null)
        {
            // ds: 日期+时间, d: 日期, s: 时间, s3: 秒后保留3位小数, s1: 秒后一位小数
            // 毫秒: yyyy-MM-dd hh:mm:ss.ffffff
            // yyyyMMdd -> 20220626 无分隔符的日期
            // yyyyMMddhhmmss -> 无分隔符的日期时间

            string time_format = "";
            if (!conv_to_bool(dtype) || dtype == "ds")      // 默认
            {
                time_format = "yyyy-MM-dd hh:mm:ss";
            }
            else if (dtype == "ms")     // 毫秒
            {
                time_format = "yyyy-MM-dd hh:mm:ss.fff";
            }
            else if (dtype == "us")     // 微秒
            {
                time_format = "yyyy-MM-dd hh:mm:ss.ffffff";
            }
            else if (dtype == "d")      // 天
            {
                time_format = "yyyy-MM-dd";
            }
            else if (dtype == "s")      // 秒
            {
                time_format = "yyyy-MM-dd hh:mm:ss";
            }
            else if (dtype.StartsWith("s"))
            {
                int times = _get_s_times(dtype);
                string new_ss = string.Concat(System.Linq.Enumerable.Repeat("f", times));
                time_format = "yyyy-MM-dd hh:mm:ss" + "." + new_ss;
                // BdUtils.log("--- times: ", times, "--- time_format: ", time_format);
            }
            else
                time_format = dtype;

            char ch = ' ';
            if (get_date != null)
            {
                bool _get_date = BdUtils.conv_to_bool(get_date);
                if (!_get_date && time_format.Contains(ch))
                {
                    var sp = time_format.Split(ch);
                    time_format = sp[sp.Length - 1];
                }
                else if (_get_date && time_format.Contains(ch))
                {
                    var sp = time_format.Split(ch);
                    time_format = sp[0];
                }
            }

            return time_format;
        }
        public static string get_time_string(string dtype = "yyyy-MM-dd hh:mm:ss", bool? get_date = null)
        {
            // ds: 日期+时间, d: 日期, s: 时间
            // 毫秒: yyyy-MM-dd hh:mm:ss.ffffff
            DateTime dt = DateTime.Now;
            return dt.ToString(get_time_format(dtype: dtype, get_date: get_date));
        }

        public static string get_time_int(string dtype = "yyyyMMddhhmmssffffff")
        {
            var ret = get_time_string(dtype: dtype);
            return ret;
        }

        static int _get_s_times(string ss)
        {
            int times = 0;
            if (ss == "s")
                return times;

            if (ss.StartsWith("s"))
            {
                int? _times = BdUtils.conv_to_int(ss.Substring(1, ss.Length - 1));
                if (_times != null)
                    times = (int)_times;
            }
            return times;
        }

        public static int get_random_number(int a = 0, int b = 10, int? seed = null)  // 取值范围: [a, b)
        {
            System.Random rd;
            if (seed == null)
            {
                rd = new System.Random(Guid.NewGuid().GetHashCode());     // 解决并发问题
            }
            else
            {
                rd = new System.Random(Convert.ToInt32(seed));
            }
            var ret = rd.Next(a, b);
            return ret;
        }
    }

    // get_random_number = TimeUtils.get_random_number;

    public static float get_random_float(float a = 0f, float b = 1f, int decimal_place = 6)
    {
        int digit_capacity = (int)(Math.Pow(10, decimal_place));
        // 先生成一个[0~1]区间的小数
        float ret = (float)get_random_number(0, digit_capacity + 1) / digit_capacity;
        // ret = b * ret + a;
        ret = ret * (b - a) + a;  // 根据归一化公式, 反推应得的值
        return ret;
    }

    /// <summary>
    ///  获取一个[a, b)区间的整数
    /// </summary>
    /// <param name="a">下限</param>
    /// <param name="b">上限(开区间, 无法取到)</param>
    /// <param name="seed">种子</param>
    /// <returns></returns>
    public static int get_random_number(int a = 0, int b = 10, int? seed = null)
    {
        return TimeUtils.get_random_number(a: a, b: b, seed: seed);

    }

    public static T get_obj_index_by_name<T>(string obj_name, int index = -1, string sep = "_")
    {
        T ret;

        BdUtils.assert(obj_name.Contains(sep), $"`obj_name[{obj_name}]`必须包含`sep[{sep}]`!");
        var ls = obj_name.Split(sep);

        string value = "";
        try
        {
            int _index;
            if (index < 0)
                _index = ls.Length + index;
            else
                _index = index;

            value = ls[_index];

            ret = (T)Convert.ChangeType(value, typeof(T));
        }
        catch (UnityException e)
        {
            throw new UnityException($"*** value[{value}]转换为[{typeof(T)}]类型失败! error: {e}");
        }
        return ret;
    }

    public static int get_obj_index_by_name(string obj_name, int index = -1, string sep = "_")
    {
        int ret = get_obj_index_by_name<int>(obj_name, index = -1, sep = "_");
        return ret;

    }

    public class IJson
    {
        public override string ToString()
        {
            return JsonConvert.SerializeObject(this);
        }
    }

    public static object getattr(object obj, string attr, object _default = null)
    {
        Type type = obj.GetType();
        var prop = type.GetProperty(attr);
        var value = prop.GetValue(obj);
        return value;
    }

    public static bool hasattr(object obj, string attr)
    {
        Type type = obj.GetType();
        var prop = type.GetProperty(attr);
        bool ret = !BdUtils.is_null(prop);
        return ret;
    }

    public static void setattr(object obj, string attr, object value = null)
    {
        Type type = obj.GetType();
        var prop = type.GetProperty(attr);
        BdUtils.assert(!BdUtils.is_null(prop), $"Type[{type}]的属性名[{attr}]对应的prop为空!");

        object convertedValue;
        try
        {
            convertedValue = Convert.ChangeType(value, prop.PropertyType);
        }
        catch (Exception)
        {
            throw new Exception($"[setattr.Convert.ChangeType] --- 将值[{value}]转换[{prop.PropertyType}]类型失败!");
        }
        // 将转换后的值赋值给属性
        prop.SetValue(obj, convertedValue);
        return;
    }

    public class ReflectionUtils
    {

        public static void show_properties(object obj_i)
        {
            var obj_type = obj_i.GetType();
            var props = obj_type.GetProperties();

            Console.WriteLine($"------------ show_properties --------- props: {props}");
            foreach (var item in props)
            {
                var value = item.GetValue(obj_i);
                Console.WriteLine($"prop: {item.Name} --- Value: {value} --- ValueType: {value.GetType()}");
            }
        }

        public static PropertyInfo[] get_properties(object obj)
        {
            Type type = obj.GetType(); // 获取 MyClass 类型的 Type 对象
            var props = type.GetProperties();
            return props;
        }

        public static Type get_property_type(object obj, string property_name)
        {
            Type type = obj.GetType();
            PropertyInfo prop = type.GetProperty(property_name);
            BdUtils.assert(!BdUtils.is_null(prop), $"Type[{type}]的属性名[{property_name}]对应的prop为空!");
            Type propType = prop.PropertyType;
            return propType;
        }


    }

    public class FileUtils
    {
        #region 文件读取与保存

        /// <summary>
        /// 获取文件中的数据串
        /// </summary>
        public static string fileToString(String filePath)
        {
            string str = "";

            //获取文件内容
            if (System.IO.File.Exists(filePath))
            {
                System.IO.StreamReader file1 = new System.IO.StreamReader(filePath);//读取文件中的数据
                str = file1.ReadToEnd();                                            //读取文件中的全部数据

                file1.Close();
                file1.Dispose();
            }
            return str;
        }

        /// <summary>
        /// 保存数据data到文件处理过程，返回值为保存的文件名
        /// </summary>
        public static String SaveProcess(String data, String name, String save_dirpath = null, bool create_if_not_exist = false)
        {
            // save_dirpath: 可以选择[temp, resource]两种路径模式, 也可自定义
            string dirname = BdUtils.base_temp_dir_path;     // 默认为临时文件路径

            if (save_dirpath != null)
            {
                if (save_dirpath == "temp")
                {
                    dirname = BdUtils.base_temp_dir_path;
                }
                else if (save_dirpath == "resource")
                {
                    dirname = BdUtils.base_resource_dir_path;
                }
                else
                {
                    dirname = save_dirpath;
                }
            }

            if (!System.IO.Directory.Exists(dirname))
            {
                if (create_if_not_exist)
                {
                    System.IO.Directory.CreateDirectory(dirname);   //该路径不存在时，在当前文件目录下创建文件夹"导出.."
                }
                else
                {
                    throw new Exception("目标文件夹路径不存在! dirpath:" + dirname);
                }
            }

            //不存在该文件时先创建
            String filePath = BdUtils.join(dirname, name);
            System.IO.StreamWriter file1 = new System.IO.StreamWriter(filePath, false);     //文件已覆盖方式添加内容

            file1.Write(data);                                                              //保存数据到文件

            file1.Close();                                                                  //关闭文件
            file1.Dispose();                                                                //释放对象

            return filePath;
        }

        #endregion
    }

    
    // 判断是否为 Windows 系统的函数
    public static bool is_windows_platform()
    {
        // 获取当前操作系统信息
        PlatformID platform = Environment.OSVersion.Platform;

        // 检查是否为 Windows
        var res = platform == PlatformID.Win32NT || platform == PlatformID.Win32Windows;
        return res;
    }
    
    /// <summary>
    /// 系统Shell命令
    /// </summary>
    public class CommandUtils
    {
        public static void run_cmd(string command, bool isFile = false)
        {
            if (is_windows_platform())
            {
                run_batch(command, isFile);
            }
            else
            {
                run_shell(command, isFile);
            }
        }
        public static (string, string) run_batch(string command, bool isFile=false)
        {
            System.Diagnostics.Process process = new System.Diagnostics.Process();

            System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();

            if (isFile)
            {
                // 如果是执行文件，则将命令解释为要执行的可执行文件的路径
                startInfo.FileName = command;
            }
            else
            {
                // 如果是执行命令，则使用 cmd.exe 来执行命令
                startInfo.FileName = "cmd.exe";
                startInfo.Arguments = $"/C {command}";
            }

            startInfo.RedirectStandardOutput = true;
            startInfo.RedirectStandardError = true;
            startInfo.UseShellExecute = false;
            startInfo.CreateNoWindow = true;

            process.StartInfo = startInfo;

            // 启动进程
            process.Start();

            // 读取输出
            string output = process.StandardOutput.ReadToEnd();
            string error = process.StandardError.ReadToEnd();

            // 等待进程执行完毕
            process.WaitForExit();

            // 输出执行结果
            return (output, error);
            // BdUtils.log("Output:");
            // BdUtils.log(output);
            //
            // BdUtils.log("Error:");
            // BdUtils.log(error);
        }

        public static string add_2_quotes(string text, string quote_type = "\"")
        {
            string ret = quote_type + text + quote_type;
            return ret;
        }

        /// <summary>
        /// Bash命令
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public static string run_bash(string command)
        {
            var escapedArgs = command.Replace("\"", "\\\"");
            //System.Diagnostics.Process
            var process = new System.Diagnostics.Process()
            {
                StartInfo = new System.Diagnostics.ProcessStartInfo
                {
                    FileName = "/bin/bash",
                    Arguments = $"-c \"{escapedArgs}\"",
                    RedirectStandardOutput = true,
                    UseShellExecute = false,
                    CreateNoWindow = true,
                }
            };
            process.Start();
            string result = process.StandardOutput.ReadToEnd();
            process.WaitForExit();
            process.Dispose();
            return result;
        }

        public static string run_shell(string cmd, bool is_file = false)
        {
            string output = string.Empty;
            string FileName = "/bin/bash";
            string Arguments = cmd;

            if (!is_file)
            {
                // 不是文件而是原生cmd的时候, 生成一个临时的shell文件来执行
                // string time_str = BdUtils.TimeUtils.time(true).ToString();
                string time_str = BdUtils.TimeUtils.get_unique_suffix();
                var temp_file_name = "temp_file__" + time_str + ".sh";

                var temp_file_path = BdUtils.FileUtils.SaveProcess(cmd, temp_file_name, save_dirpath: BdUtils.base_temp_dir_path, create_if_not_exist: true);

                Arguments = temp_file_path;
                // BdUtils.py_print("temp_file_path --- ", temp_file_path, "~~~ File.Exists: ", File.Exists(temp_file_path));
            }

            // --- unify directory format
            Arguments = add_2_quotes(Arguments);

            System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo()
            {
                FileName = FileName,
                Arguments = Arguments,
                UseShellExecute = false,
                RedirectStandardOutput = true,
                CreateNoWindow = true
            };

            using (var process = System.Diagnostics.Process.Start(startInfo))
            {
                output = process.StandardOutput.ReadToEnd();
                process.WaitForExit();
                // process.Close();
                // process.Dispose();
            }
            return output;
        }

        static public System.Threading.Tasks.Task<int> RunProcessAsync(string fileName)
        {
            string output = string.Empty;
            string FileName = "/bin/bash";
            string Arguments = fileName;

            var tcs = new System.Threading.Tasks.TaskCompletionSource<int>();

            //System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo()
            //{
            //    FileName = FileName,
            //    Arguments = "\"" + Arguments + "\"",
            //    //UseShellExecute = false,
            //    //RedirectStandardOutput = true,
            //    //CreateNoWindow = true
            //};

            // --- win ---
            //var process = new System.Diagnostics.Process
            //{
            //    StartInfo = { FileName = fileName },
            //    EnableRaisingEvents = true
            //};

            //var process = System.Diagnostics.Process.Start(startInfo);
            var process = new System.Diagnostics.Process
            {
                StartInfo = {
                    FileName = FileName,
                    Arguments = "\"" + Arguments + "\""
                },
                EnableRaisingEvents = true
            };

            process.Exited += (sender, args) =>
            {
                tcs.SetResult(process.ExitCode);
                process.Dispose();
            };

            process.Start();

            return tcs.Task;
        }
    }
    
    
    // 检查`input`是否符合`{{ xx }}`之类的模板匹配格式
    private static bool conform_template_match_format(string input)
    {
        // 定义正则表达式模式
        string pattern = @"\{\{.*?\}\}";

        // 使用 Regex.IsMatch 方法检查字符串是否包含特殊符号
        return Regex.IsMatch(input, pattern);
    }
    public static string replace_template_variables(string template, Dictionary<string, object> variables = null, bool raise_error=false)
    {
        if (!conform_template_match_format(template))
            return template;
        
        if (variables == null || variables.Count == 0)
            return template;

        // 使用正则表达式匹配模板中的占位符，例如 "{{x}}"
        Regex regex = new Regex(@"{{\s*([^}]+)\s*}}");

        // 使用 MatchEvaluator 来定义替换规则
        string result = regex.Replace(template, match =>
        {
            // 获取占位符中的键名，例如 "x"
            string key = match.Groups[1].Value.Trim();

            // 根据键名从字典中获取对应的值
            if (variables.ContainsKey(key))
            {
                // return variables[key].ToString();
                return BdUtils.conv_to_string(variables[key]);
            }
            else
            {
                BdUtils.assert(!raise_error, $"模板变量key[{key}]不存在?");
                return match.Value; // 如果键名不存在，则保留原始占位符
            }
        });

        return result;
    }
}

// public class Debug
// {
//     public static void Log(params object[] list)
//     {
//         BdUtils.log(list);
//     }
//     public static void Log(object text)
//     {
//         BdUtils.log(text);
//     }
// }

// #nullable disable
